<?php
// +----------------------------------------------------------------------
// | Bwsaas
// +----------------------------------------------------------------------
// | Copyright (c) 2015~2020 http://www.buwangyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Gitee ( https://gitee.com/buwangyun/bwsaas )
// +----------------------------------------------------------------------
// | Author: buwangyun <hnlg666@163.com>
// +----------------------------------------------------------------------
// | Date: 2020-9-28 10:55:00
// +----------------------------------------------------------------------

namespace app;

use buwang\exception\MiniappException;
use buwang\traits\JsonTrait;
use buwang\traits\JumpTrait;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use Firebase\JWT\ExpiredException;

use Firebase\JWT\SignatureInvalidException;
use Firebase\JWT\BeforeValidException;
use \UnexpectedValueException;
use buwang\exception\AuthException;

use think\Response;
use Throwable;

/**
 * 应用异常处理类
 */
class ExceptionHandle extends Handle
{
    use JsonTrait;
    use JumpTrait;

    /**
     * 不需要记录信息（日志）的异常类列表
     * @var array
     */
    protected $ignoreReport = [
        HttpException::class,
        HttpResponseException::class,
        ModelNotFoundException::class,
        DataNotFoundException::class,
        ValidateException::class,
    ];

    /**
     * 记录异常信息（包括日志或者其它方式记录）
     *
     * @access public
     * @param Throwable $exception
     * @return void
     */
    public function report(Throwable $exception): void
    {
        // 使用内置的方式记录异常日志
        parent::report($exception);
    }

    protected function convertExceptionToArray(Throwable $exception):array {
        if ($this->app->isDebug()) {
            // 调试模式，获取详细的错误信息
            $traces        = [];
            $nextException = $exception;
            do {
                $traces[] = [
                    'name'    => get_class($nextException),
                    'file'    => $nextException->getFile(),
                    'line'    => $nextException->getLine(),
                    'code'    => $this->getCode($nextException),
                    'message' => $this->getMessage($nextException),
                    'trace'   => $nextException->getTrace(),
                    'source'  => $this->getSourceCode($nextException),
                ];
            } while ($nextException = $nextException->getPrevious());
            $data = [
                'code'    => $this->getCode($exception),
                'message' => $this->getMessage($exception),
                'traces'  => $traces,
                'datas'   => $this->getExtendData($exception),
                'tables'  => [
                    'GET Data'            => $this->app->request->get(),
                    'POST Data'           => $this->app->request->post(),
                    // 'Files'               => $this->app->request->file(),
                    'Cookies'             => $this->app->request->cookie(),
                    'Session'             => $this->app->exists('session') ? $this->app->session->all() : [],
                    'Server/Request Data' => $this->app->request->server(),
                ],
            ];
        } else {
            // 部署模式仅显示 Code 和 Message
            $data = [
                'code'    => $this->getCode($exception),
                'message' => $this->getMessage($exception),
            ];

            if (!$this->app->config->get('app.show_error_msg')) {
                // 不显示详细错误信息
                $data['message'] = $this->app->config->get('app.error_message');
            }
        }

        return $data;
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @access public
     * @param \think\Request $request
     * @param Throwable $e
     * @return Response
     */
    public function render($request, Throwable $e): Response
    {
        // 添加自定义异常处理机制

        //token jwt验证
        if ($e instanceof UnexpectedValueException) {
            return $this->code(422)->error($e->getMessage(), [], 400422);
        }
        if ($e instanceof SignatureInvalidException) {
            // return json("Incorrect signature", 422);//签名不正确
            return $this->code(422)->error("Incorrect signature", [], 400422);
        }
        if ($e instanceof BeforeValidException) {
            //return json("The signature will take some time to us", 422);//签名尚未生效,签名在某个时间点之后才能用
            return $this->code(422)->error("The signature will take some time to us", [], 400422);
        }
        if ($e instanceof ExpiredException) {
            // return json("Token has expired", 422);//token已经过期
            //var_dump($e->getFile(),$e->getline());die;
            return $this->code(422)->error("Token has expired", [], 400422);
        }

        //JWT token解析错误
        if ($e instanceof \DomainException) {
            return $this->code(422)->error("Token error");
        }

        // 参数验证错误
        if ($e instanceof ValidateException) {
            $statusCode = $e->getCode() ?: 200;
            return $this->code($statusCode)->error($e->getError());
        }
        //应用状态异常
        if ($e instanceof MiniappException) {
            $statusCode = $e->getCode() ?: 200;
            if(request()->isAjax()) return $this->code($statusCode)->error($e->getMessage());
            return $this->error_jump($e->getMessage(),NOT_JUMP);
        }
        // 请求异常
        if ($e instanceof HttpException && ($request->isGet() || $request->isPost())) {
            //return response($e->getMessage(), $e->getStatusCode());
            return $this->code(500)->error($e->getMessage());
        }
        //捕获response异常
        if ($e instanceof HttpResponseException) {
            return $e->getResponse();
        }

        //权限认证异常错误
        if ($e instanceof AuthException) {
            $statusCode = $e->getCode() ?: 200;
            if(request()->isAjax()) return $this->code($statusCode)->error($e->getMessage());
            return $this->error_jump($e->getMessage(),NOT_JUMP);
        }

        //其它错误自定义处理
        if($request->isAjax() && ($e instanceof \Throwable)){
            trace(["msg"=>$e->getMessage(),"file"=>$e->getFile(),"line"=>$e->getLine()],'error');
            return $this->code(500)->error($e->getMessage().':'.$e->getFile().':'.$e->getLine());
        }
        // 其他错误交给系统处理
        return parent::render($request, $e);
    }
}
